﻿#include "pch.h"
#include "sbcs.h"
#include "sbmath.h"

#if !_SB_MOBILE_
// 테이블
extern const kushort _k_rand_tbl[65536];
#endif

// 무작위
static kuint _k_rand_seed = 0;


//////////////////////////////////////////////////////////////////////////
// 난수

/**
 * 난수 시드를 만든다.
 * @param [입력,반환]	seed	(널값이 아님) 난수 시드.
 * @param	genseed			특정 시드를 만드려면 값을 넣는다.
 * @param	addtip			난수를 더 복잡하게 하려면 사용한다.
 */
void k_snans(kuint* seed, kuint genseed, kcham addtip)
{
	kuint i, z;

	if (genseed == 0)
		genseed = (kuint)k_timedhash();

	if (addtip)
	{
		z = genseed % 11;
		for (*seed = *seed + genseed, i = 0; i < z; i++)
			*seed = *seed * 214013L + 2531011L;
	}
	else
	{
		*seed = *seed + genseed;
		*seed = *seed * 214013L + 2531011L;
	}
}

/**
 * 임의의 정수 난수를 얻는다.
 * @param [입력,반환]	seed	(널값이 아님) 난수 시드.
 * @return	난수 값.
 */
kint k_nans(kuint* seed)
{
#if !_SB_MOBILE_
	if (!seed)
		return 0;

	*seed = *seed * 214013L + 2531011L;
	return _k_rand_tbl[(*seed >> 16) & 0xFFFF];
#else
	return k_nans_value(seed);
#endif
}

/**
 * 난수 테이블을 사용하지 않고 임의의 정수 난수를 얻는다.
 * @param [입력,반환]	seed	(널값이 아님) 난수 시드.
 * @return	난수 값.
 */
kint k_nans_value(kuint* seed)
{
	if (!seed)
		return 0;

	*seed = *seed * 214013L + 2531011L;
	return (*seed >> 16) & 0x7FFF;
}

/**
 * 정수 범위에서 난수를 얻는다.
 * @param [입력,반환]	seed	(널값이 아님) 난수 시드.
 * @param	min				최소.
 * @param	max				최대.
 * @return	난수 값.
 */
kint k_nans_int(kuint* seed, kint min, kint max)
{
	if (min == max)
		return min;
	else if (min < max)
		return (kint)((double)k_nans(seed) / (K_MAX_RAND + 1)*(max - min) + min);
	else
		return (kint)((double)k_nans(seed) / (K_MAX_RAND + 1)*(min - max) + max);
}

/**
 * 64비트 정수 범위에서 난수를 얻는다.
 * @param [입력,반환]	seed	(널값이 아님) 난수 시드.
 * @param	min				최소.
 * @param	max				최대.
 * @return	난수 값.
 */
klong k_nans_long(kuint* seed, klong min, klong max)
{
	if (min == max)
		return min;
	else if (min < max)
		return (klong)((double)k_nans(seed) / (K_MAX_RAND + 1)*(max - min) + min);
	else
		return (klong)((double)k_nans(seed) / (K_MAX_RAND + 1)*(min - max) + max);
}

/**
 * 부호없는 정수 범위에서 난수를 얻는다.
 * @param [입력,반환]	seed	(널값이 아님) 난수 시드.
 * @param	min				최소.
 * @param	max				최대.
 * @return	난수 값.
 */
kuint k_nans_uint(kuint* seed, kuint min, kuint max)
{
	if (min == max)
		return min;
	else if (min < max)
		return (kuint)((double)k_nans(seed) / (K_MAX_RAND + 1)*(max - min) + min);
	else
		return (kuint)((double)k_nans(seed) / (K_MAX_RAND + 1)*(min - max) + max);
}

/**
 * 부호없는 64비트 정수 범위에서 난수를 얻는다.
 * @param [입력,반환]	seed	(널값이 아님) 난수 시드.
 * @param	min				최소.
 * @param	max				최대.
 * @return	난수 값.
 */
kulong k_nans_ulong(kuint* seed, kulong min, kulong max)
{
	if (min == max)
		return min;
	else if (min < max)
		return (kulong)((double)k_nans(seed) / (K_MAX_RAND + 1)*(max - min) + min);
	else
		return (kulong)((double)k_nans(seed) / (K_MAX_RAND + 1)*(min - max) + max);
}

/**
 * 실수 범위에서 난수를 얻는다.
 * @param [입력,반환]	seed	(널값이 아님) 난수 시드.
 * @param	min				최소.
 * @param	max				최대.
 * @return	난수 값.
 */
float k_nans_single(kuint* seed, float min, float max)
{
	if (min < max)
		return (float)((double)k_nans(seed) / ((float)K_MAX_RAND + 1.0f)*(max - min) + min);
	else if (max < min)
		return (float)((double)k_nans(seed) / ((float)K_MAX_RAND + 1.0f)*(min - max) + max);
	else
		return min;
}

/**
 * 배정도 실수 범위에서 난수를 얻는다.
 * @param [입력,반환]	seed	(널값이 아님) 난수 시드.
 * @param	min				최소.
 * @param	max				최대.
 * @return	난수 값.
 */
double k_nans_double(kuint* seed, double min, double max)
{
	if (min < max)
		return ((double)k_nans(seed) / (K_MAX_RAND + 1.0)*(max - min) + min);
	else if (max < max)
		return ((double)k_nans(seed) / (K_MAX_RAND + 1.0)*(min - max) + max);
	else
		return min;
}


//////////////////////////////////////////////////////////////////////////
// 난수

/**
 * 난수 시드를 지정한다
 * @param	seed	난수 시드로 지정할 값.
 */
void k_srand(kuint seed)
{
	kuint i, z;

	if (seed == 0)
		seed = (kuint)k_timedhash();

	z = seed % 11;
	for (_k_rand_seed = (_k_rand_seed + seed), i = 0; i < z; i++)
		_k_rand_seed = _k_rand_seed * 214013L + 2531011L;
}

/**
 * 난수 값을 얻는다
 * @return	난수 값.
 */
kint k_rand(void)
{
	return k_nans(&_k_rand_seed);
}

/**
 * 난수 값을 테이블을 사용하지 않고 얻는다.
 * @return	난수 값.
 */
kint k_rand_value(void)
{
	return k_nans_value(&_k_rand_seed);
}

/**
 * 정수 범위에서 난수 값을 얻는다.
 * @param	min	최소.
 * @param	max	최대.
 * @return	난수 값.
 */
kint k_rand_int(kint min, kint max)
{
	return k_nans_int(&_k_rand_seed, min, max);
}

/**
 * 64비트 정수 범위에서 난수 값을 얻는다.
 * @param	min	최소.
 * @param	max	최대.
 * @return	난수 값.
 */
klong k_rand_long(klong min, klong max)
{
	return k_nans_long(&_k_rand_seed, min, max);
}

/**
 * 부호없는 정수 범위에서 난수 값을 얻는다.
 * @param	min	최소.
 * @param	max	최대.
 * @return	난수 값.
 */
kuint k_rand_uint(kuint min, kuint max)
{
	return k_nans_uint(&_k_rand_seed, min, max);
}

/**
 * 부호없는 64비트 정수 범위에서 난수 값을 얻는다.
 * @param	min	최소.
 * @param	max	최대.
 * @return	난수 값.
 */
kulong k_rand_ulong(kulong min, kulong max)
{
	return k_nans_ulong(&_k_rand_seed, min, max);
}

/**
 * 실수 범위에서 난수 값을 얻는다.
 * @param	min	최소.
 * @param	max	최대.
 * @return	난수 값.
 */
float k_rand_single(float min, float max)
{
	return k_nans_single(&_k_rand_seed, min, max);
}

/**
 * 배정도 실수 범위에서 난수 값을 얻는다.
 * @param	min	최소.
 * @param	max	최대.
 * @return	난수 값.
 */
double k_rand_double(double min, double max)
{
	return k_nans_double(&_k_rand_seed, min, max);
}


//////////////////////////////////////////////////////////////////////////
// 메르센 트위스터
//		http://www.math.sci.hiroshima-u.ac.jp/~m-mat/MT/emt.html
//		http://www-personal.umich.edu/~wagnerr/MersenneTwister.html
#define _MRT_LENGTH		624
#define _MRT_PERIOD		397
#define _MRT_DIMENSION	_MRT_LENGTH-1

struct kMrsn
{
	kuint*				next;
	kint				left;
	kint				len;
	kuint				state[_MRT_LENGTH];
};

#define _k_mrsn_hibit(v)				((v)&0x80000000U)
#define _k_mrsn_lobit(v)				((v)&0x00000001U)
#define _k_mrsn_lobits(v)				((v)&0x7FFFFFFFU)
#define _k_mrsn_maxbits(u, v)			(_k_mrsn_hibit(u)|_k_mrsn_lobits(v))
#define _k_mrsn_mad(v)					(_k_mrsn_lobit(v) ? 0x9908b0dfU : 0)

// 꼬기
static kuint _k_mrsn_twist(const kuint m, const kuint s0, const kuint s1)
{
	return m ^ (_k_mrsn_maxbits(s0, s1) >> 1) ^ _k_mrsn_mad(s1);
}

// 만들기
static void _k_mrsn_init_gen(kMrsn* self)
{
	static const int l_m_p = (int)_MRT_PERIOD - (int)_MRT_LENGTH;
	kuint* p = self->state;
	int i;

	for (i = _MRT_LENGTH - _MRT_PERIOD; i--; ++p)
		*p = _k_mrsn_twist(p[_MRT_PERIOD], p[0], p[1]);

	for (i = _MRT_PERIOD; --i; ++p)
		*p = _k_mrsn_twist(p[l_m_p], p[0], p[1]);

	*p = _k_mrsn_twist(p[l_m_p], p[0], self->state[0]);
	self->left = _MRT_LENGTH;
	self->next = self->state;
}

/**
 * 메르센 트위스터를 만든다.
 * @return	메르센 트위스터 포인터. 이 값은 k_free 함수로 제거할 수 있다.
 */
kMrsn* k_mrsn_alloc(void)
{
	return k_new_0(kMrsn);
}

/**
 * 메르센 트위스터에 시드를 넣는다.
 * @param [입력]	self	개체나 인터페이스의 자기 자신 값.
 * @param	seed		난수 시드.
 */
void k_mrsn_seed(kMrsn* self, kuint seed)
{
	kuint* s = self->state;
	kuint* r = self->state;
	int i;

	if (seed == 0)
		seed = (kuint)k_timedhash();

	*s++ = seed&K_MAX_UINT;
	for (i = 1; i < _MRT_LENGTH; i++)
	{
		*s++ = (1812433253U * (*r ^ (*r >> 30)) + i)&K_MAX_UINT;
		r++;
	}

	self->len = _MRT_LENGTH;
	_k_mrsn_init_gen(self);
}

/**
 * 메르센 트위스터로 부호없는 정수를 얻는다.
 * @param [입력]	self	개체나 인터페이스의 자기 자신 값.
 * @return	난수 값.
 */
kuint k_mrsn_uint(kMrsn* self)
{
	kuint n;

	if (self->left == 0)
		_k_mrsn_init_gen(self);
	self->left--;

	n = *self->next++;
	n ^= (n >> 11);
	n ^= (n << 7) & 0x9D2C5680U;
	n ^= (n << 15) & 0xEFC60000U;

	return (n ^ (n >> 18));
}

/**
 * 메르센 트위스터로 부호없는 64비트 정수를 얻는다.
 * @param [입력]	self	개체나 인터페이스의 자기 자신 값.
 * @return	난수 값.
 */
kulong k_mrsn_ulong(kMrsn* self)
{
	kvlong n;
	n.dw.l = k_mrsn_uint(self);
	n.dw.h = k_mrsn_uint(self);
	return n.q;
}

/**
 * [0,1] 범위에서 메르센 트위스터 값을 얻는다.
 * @param [입력]	self	개체나 인터페이스의 자기 자신 값.
 * @return	난수 값.
 */
double k_mrsn_open_open(kMrsn* self)
{
	double d = (double)k_mrsn_uint(self);
	return d*(1.0 / 4294967295.0);
}

/**
 * (0,1) 범위에서 메르센 트위스터 값을 얻는다.
 * @param [입력]	self	개체나 인터페이스의 자기 자신 값.
 * @return	난수 값.
 */
double k_mrsn_close_close(kMrsn* self)
{
	double d = (double)k_mrsn_uint(self);
	return (d + 0.5)*(1.0 / 4294967295.0);
}

/**
 * 53비트 메르센 트위스터 값을 얻는다 (IEEE double) -> [0,1)
 * @param [입력]	self	개체나 인터페이스의 자기 자신 값.
 * @return	난수 값.
 */
double k_mrsn_rand53(kMrsn* self)
{
	kuint a = k_mrsn_uint(self) >> 5;
	kuint b = k_mrsn_uint(self) >> 6;
	return (a*67108864.0 + b)*(1.0 / 9007199254740992.0);
}

/**
 * 가우시안 메르센 트위스터 값을 얻는다.
 * @param [입력]	self	개체나 인터페이스의 자기 자신 값.
 * @param	mn			mn.
 * @param	sd			sd.
 * @return	난수 값.
 */
double k_mrsn_gaussian(kMrsn* self, double mn, double sd)
{
	double x, y, r, s;

	do
	{
		x = 2.0*k_mrsn_open_open(self) - 1.0;
		y = 2.0*k_mrsn_open_open(self) - 1.0;
		r = x*x + y*y;
	} while (r >= 1.0 || r == 0.0);

	s = k_sqrt(-2.0*k_log(r) / r);

	return mn + x*s*sd;
}
